package com.dong.order.service;

import com.dong.basic.constant.Constant;
import com.dong.basic.enums.OrderStatusEnum;
import com.dong.basic.tools.ConnectionTools;
import com.dong.order.dao.OrderDetailDao;
import com.dong.basic.dto.OrderMessageDTO;

import com.dong.basic.po.OrderDetailPO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

import static com.dong.basic.enums.OrderStatusEnum.*;

@Service
@Slf4j
public class OrderMessageOrderService {
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private OrderDetailDao orderDetailDao;
    @Autowired
    private Channel channel;
    @Autowired
    private RabbitAdmin rabbitAdmin;


    /**
     *  设置异步
     */
    @Async
    public void handleMessage()  {
        log.warn(Thread.currentThread().getName()+"处理消息...");
        try  {
            declare2();

            while (true) {
                Thread.sleep(1000000L);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void declare1() throws IOException {
        /**dlx*/
        channel.exchangeDeclare("dlx.exchange",BuiltinExchangeType.TOPIC,true,false,null);
        channel.queueDeclare("dlx.queue",true,false,false,null);
        channel.queueBind("dlx.queue","dlx.exchange","#");
        /**restaurant */
        channel.exchangeDeclare(Constant.Exchange.RESTAURANT, BuiltinExchangeType.DIRECT,true,false,new HashMap<>());
        Map<String,Object> props = new HashMap<>(16);
        props.put("x-message-ttl",15000);
        props.put("x-max-length",100);
        props.put("x-dead-letter-exchange","dlx.exchange");


        channel.queueDeclare(Constant.Queue.ORDER,true,false,false,props);
        //绑定  饭店交换机===> order===>> 订单队列
        channel.queueBind(Constant.Queue.ORDER,Constant.Exchange.RESTAURANT,Constant.BindingKey.ORDER);


        /** deliveryman*/
        channel.exchangeDeclare(Constant.Exchange.DELIVERYMAN, BuiltinExchangeType.DIRECT,true,false,new HashMap<>());
        // 绑定  快递交换机==> order==> 订单队列
        channel.queueBind(Constant.Queue.ORDER,Constant.Exchange.DELIVERYMAN,Constant.BindingKey.ORDER);

        channel.basicConsume(Constant.Queue.ORDER,false,deliverCallback,consumerTag -> {});
        /** settlement*/
        channel.exchangeDeclare(Constant.Exchange.SETTLEMENT, BuiltinExchangeType.TOPIC,true,false,new HashMap<>());
        channel.queueDeclare(Constant.Queue.SETTLEMENT,true,false,false,null);

        // 绑定  settlement==> order==> 订单队列
        channel.queueBind(Constant.Queue.ORDER,Constant.Exchange.SETTLEMENT,Constant.BindingKey.ORDER);

        /**reward */
        channel.exchangeDeclare(Constant.Exchange.REWARD, BuiltinExchangeType.TOPIC,true,false,new HashMap<>());
        channel.queueDeclare(Constant.Queue.REWARD,true,false,false,null);
        //绑定  饭店交换机===> order===>> 订单队列
        channel.queueBind(Constant.Queue.REWARD,Constant.Exchange.REWARD,Constant.BindingKey.REWARD);
        channel.addReturnListener(returnMessage -> {
            try {
                log.warn("message has been returned!, message: {}", objectMapper.writeValueAsString(returnMessage));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        });
    }
    private void declare2() throws IOException {
        /**dlx*/
        TopicExchange dlxExchange = new TopicExchange(Constant.Exchange.DLX, true, false);
        Queue dlxQueue = new Queue(Constant.Queue.DLX, true, false, false);
        rabbitAdmin.declareQueue(dlxQueue);
        rabbitAdmin.declareExchange(dlxExchange);
        Binding dlxBinding = new Binding(Constant.Queue.DLX, Binding.DestinationType.QUEUE, Constant.Exchange.DLX, "#", null);
        rabbitAdmin.declareBinding(dlxBinding);

        /**restaurant */
        DirectExchange restaurantExchange = new DirectExchange(Constant.Exchange.RESTAURANT,true,false);
        rabbitAdmin.declareExchange(restaurantExchange);

        Map<String,Object> props = new HashMap<>(16);
        props.put("x-message-ttl",15000);
        props.put("x-max-length",100);
        props.put("x-dead-letter-exchange",Constant.Exchange.DLX);
        Queue orderQueue = new Queue(Constant.Queue.ORDER, true, false, false, props);
        rabbitAdmin.declareQueue(orderQueue);
        Binding orderRestaurantBinding = new Binding(Constant.Queue.ORDER, Binding.DestinationType.QUEUE, Constant.Exchange.RESTAURANT, "#", null);
        rabbitAdmin.declareBinding(orderRestaurantBinding);

        //绑定  饭店交换机===> order===>> 订单队列
        Binding orderBinding = new Binding(Constant.Queue.ORDER, Binding.DestinationType.QUEUE, Constant.Exchange.RESTAURANT, Constant.BindingKey.ORDER, null);
        rabbitAdmin.declareBinding(orderBinding);


        /** deliveryman*/
        DirectExchange deliverymanExchange = new DirectExchange(Constant.Exchange.DELIVERYMAN, true, false, new HashMap<>(4));
        rabbitAdmin.declareExchange(deliverymanExchange);
        Binding orderDeliveryBinding = new Binding(Constant.Queue.ORDER, Binding.DestinationType.QUEUE, Constant.Exchange.DELIVERYMAN, Constant.BindingKey.ORDER, null);
        rabbitAdmin.declareBinding(orderDeliveryBinding);

//#########################################
        channel.basicConsume(Constant.Queue.ORDER,false,deliverCallback,consumerTag -> {});

        /** settlement*/
        TopicExchange settlementExchange = new TopicExchange(Constant.Exchange.SETTLEMENT, true, false, null);
        Queue settlementQueue = new Queue(Constant.Queue.SETTLEMENT, true, false,false);
        rabbitAdmin.declareExchange(settlementExchange);
        rabbitAdmin.declareQueue(settlementQueue);
        Binding orderSettlementBinding = new Binding(Constant.Queue.ORDER, Binding.DestinationType.QUEUE, Constant.Exchange.SETTLEMENT, Constant.BindingKey.ORDER, null);
        rabbitAdmin.declareBinding(orderSettlementBinding);


        /**reward */
        TopicExchange rewardExchange = new TopicExchange(Constant.Exchange.REWARD, true, false, null);
        Queue rewardQueue = new Queue(Constant.Queue.REWARD, true, false,false);
        rabbitAdmin.declareExchange(rewardExchange);
        rabbitAdmin.declareQueue(rewardQueue);
        Binding rewardBinding = new Binding(Constant.Queue.REWARD, Binding.DestinationType.QUEUE, Constant.Exchange.REWARD, Constant.BindingKey.REWARD, null);
        rabbitAdmin.declareBinding(rewardBinding);

//#########################################

        channel.addReturnListener(returnMessage -> {
            try {
                log.warn("message has been returned!, message: {}", objectMapper.writeValueAsString(returnMessage));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        });
    }


    DeliverCallback deliverCallback = this::handle;

    private void handle(String consumerTag, Delivery message) {
        log.info("order handle msg!");
        try  {
            String messageBody = new String(message.getBody());
            // 将信息体反序列化为dto
            OrderMessageDTO dto = objectMapper.readValue(messageBody, OrderMessageDTO.class);
            final OrderDetailPO po = orderDetailDao.selectOrder(dto.getOrderId());
            switch (po.getStatus()) {
                /**
                 *
                 */
                case ORDER_CREATING:
                    //商家微服务
                    if (dto.getConfirmed() && null != dto.getPrice()) {
                        po.setStatus(ORDER_CONFIRM_RESTAURANT);
                        po.setPrice(dto.getPrice());
                        orderDetailDao.update(po);
                        String msg2Send = objectMapper.writeValueAsString(dto);
                        channel.basicPublish(Constant.Exchange.DELIVERYMAN, Constant.RoutingKey.DELIVERYMAN, null, msg2Send.getBytes());
                        if (channel.waitForConfirms(2000)) {
                            log.error("消息挂了...");
                        }
                    } else {
                        po.setStatus(OrderStatusEnum.ORDER_FAIL);
                        orderDetailDao.update(po);
                    }
                    break;
                case ORDER_CONFIRM_RESTAURANT:
                    log.warn("ORDER_CONFIRM_RESTAURANT");
                    if(null!=dto.getDeliverymanId()) {
                        po.setStatus(ORDER_CONFIRM_DELIVERYMAN);
                        po.setDeliverymanId(dto.getDeliverymanId());
                        orderDetailDao.update(po);
                        String msg2Send = objectMapper.writeValueAsString(dto);
                        channel.basicPublish(Constant.Exchange.SETTLEMENT, Constant.RoutingKey.SETTLEMENT, null, msg2Send.getBytes());
                        if (channel.waitForConfirms(2000)) {
                            log.error("消息挂了...");
                        }else {
                            log.warn("ORDER_CONFIRM_RESTAURANT FINISH");
                        }
                    }else {
                        po.setStatus(OrderStatusEnum.ORDER_FAIL);
                        orderDetailDao.update(po);
                    }
                    break;
                case ORDER_CONFIRM_DELIVERYMAN:
                    log.warn("ORDER_CONFIRM_DELIVERYMAN");
                    if(null!=dto.getSettlementId()) {
                        po.setStatus(SETTLEMENT_CONFIRMED);
                        po.setSettlementId(dto.getSettlementId());
                        orderDetailDao.update(po);
                        String msg2Send = objectMapper.writeValueAsString(dto);
                        channel.basicPublish(Constant.Exchange.REWARD, Constant.RoutingKey.REWARD, null, msg2Send.getBytes());
                        if (channel.waitForConfirms(2000)) {
                            log.error("消息挂了...");
                        }
                    }else {
                        po.setStatus(OrderStatusEnum.ORDER_FAIL);
                        orderDetailDao.update(po);
                    }
                    break;
                case SETTLEMENT_CONFIRMED:
                    log.warn("SETTLEMENT_CONFIRMED");
                    if(null!=dto.getRewardId()) {
                        po.setStatus(ORDER_CREATED);
                        po.setRewardId(dto.getRewardId());
                        orderDetailDao.update(po);
                    }else {
                        po.setStatus(OrderStatusEnum.ORDER_FAIL);
                        orderDetailDao.update(po);
                    }
                    break;
                default: {
                    break;
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        try {
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
